﻿using System;
using System.Collections.Generic;
using System.Linq;
using NHibernate.Mapping.ByCode;
using Xenta.Enums;
using Xenta.Utils;

namespace Xenta.Entities
{
    /// <summary>
    /// Represents the file group entity.
    /// </summary>
    /// <remarks><pre>
    /// The entity regarding the disclosure level.
    /// 
    /// + Entire
    ///  | - Files(of the Base level)
    ///  + Extended
    ///   + Base
    ///    | - Owner(of the Base level)
    ///    | - Flags
    ///    | - UpdatedOn
    ///    | - CreatedOn
    ///    | - Translations(of the same level)
    ///    + Keys
    ///     + Primary
    ///      | - EntityID
    /// 
    /// Note: upper level includes lower level.
    /// </pre></remarks>
    public class FileGroupEntity : AuditableEntity,
        IRelationHolder<FileEntity>
    {
        #region Fields

        private ICollection<FileEntity> _files;
        private ICollection<TFileGroupEntity> _translations;

        #endregion

        #region Ctors

        /// <summary>
        /// Initializes a new class instance.
        /// </summary>
        public FileGroupEntity()
        {
            _files = new List<FileEntity>();
            _translations = new List<TFileGroupEntity>();
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the owner.
        /// </summary>
        public virtual FileOwnerEntity Owner
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the flags.
        /// </summary>
        public virtual FileGroupFlags Flags
        {
            get;
            set;
        }

        #endregion

        #region Collections

        /// <summary>
        /// Gets or sets the file collection.
        /// </summary>
        public virtual ICollection<FileEntity> Files
        {
            get
            {
                return _files;
            }
            set
            {
                _files = value;
            }
        }

        /// <summary>
        /// Gets or sets the group translations.
        /// </summary>
        public virtual ICollection<TFileGroupEntity> Translations
        {
            get
            {
                return _translations;
            }
            set
            {
                _translations = value;
            }
        }

        #endregion

        #region Methods

        /// <summary>
        /// Gets the group translation.
        /// </summary>
        /// <param name="languageID">The language identifier.</param>
        /// <returns>The group translation.</returns>
        public virtual TFileGroupEntity Translation(int languageID)
        {
            return _translations.SingleOrDefault(x => x.Language.EntityID == languageID);
        }

        /// <summary>
        /// Maps a file to the group.
        /// </summary>
        /// <param name="child">The file to map.</param>
        public virtual void Map(FileEntity child)
        {
            if(Files.Contains(child))
                return;
            Files.Add(child);
        }

        /// <summary>
        /// Unmaps the file from the group.
        /// </summary>
        /// <param name="child">The file to unmap.</param>
        public virtual void Unmap(FileEntity child)
        {
            if(!Files.Contains(child))
                return;
            Files.Remove(child);
        }

        #endregion

        #region Utilities

        protected override void Disclose(IDictionary<String, Object> obj,
            DisclosureLevel level,
            IDictionary<String, String> options)
        {
            base.Disclose(obj, level, options);
            if(level >= DisclosureLevel.Base)
            {
                obj["Owner"] = Owner.Disclose(DisclosureLevel.Base, options);
                obj["Flags"] = Flags.ToString();
                obj["Translations"] = (options.ContainsKey("TranslatedTo")
                    ? Translations.Where(x => options["TranslatedTo"].Contains(x.Language.Code))
                    : Translations)
                    .Map(x => x.Disclose(level, options));
            }
            if(level >= DisclosureLevel.Entire)
                obj["Files"] = Files.Map(x => x.Disclose(DisclosureLevel.Base, options));
        }

        #endregion
    }

    /// <summary>
    /// The entity NHibernate mapping.
    /// </summary>
    public class FileGroupMapping : EntityMapping<FileGroupEntity>
    {
        /// <summary>
        /// Initializes a new class instance.
        /// </summary>
        public FileGroupMapping()
        {
            Table("fwk_FileGroups");
            Id(x => x.EntityID, m =>
            {
                m.Column("GroupID");
                m.Generator(Generators.Identity);
            });
            ManyToOne(x => x.Owner, m =>
            {
                m.Column("OwnerID");
                m.ForeignKey("FK_fwk_FileGroups_fwk_FileOwners");
                m.NotNullable(true);
                m.Fetch(FetchKind.Join);
            });
            Property(x => x.Flags, m =>
            {
                m.NotNullable(true);
            });
            Property(x => x.CreatedOn, m =>
            {
                m.NotNullable(true);
            });
            Property(x => x.UpdatedOn, m =>
            {
                m.NotNullable(true);
            });
            Bag(x => x.Files, m =>
            {
                m.Table("fwk_MFileGroups");
                m.Key(k =>
                {
                    k.Column("GroupID");
                    k.ForeignKey("FK_fwk_MFileGroups_fwk_FileGroups");
                    k.NotNullable(true);
                });
            },
            a =>
            {
                a.ManyToMany(m =>
                {
                    m.Column("FileID");
                    m.ForeignKey("FK_fwk_MFileGroups_fwk_Files");
                });
            });
            Bag(x => x.Translations, m =>
            {
                m.Table("fwk_TFileGroups");
                m.Key(k =>
                {
                    k.Column("GroupID");
                    k.ForeignKey("FK_fwk_TFileGroups_fwk_FileGroups");
                    k.NotNullable(true);
                });
                m.Inverse(true);
                m.BatchSize(10);
            },
            a =>
            {
                a.OneToMany();
            });
        }
    }
}
